<Project>

  <PropertyGroup>
    <!-- Custom trimming logic should always use live ILLink, even when none of the
         trimming-related SDK properties are set. -->
    <_RequiresLiveILLink>true</_RequiresLiveILLink>
  </PropertyGroup>

  <PropertyGroup>
    <IsTrimmable Condition="'$(IsTrimmable)' == ''">true</IsTrimmable>
    <PrepareResourcesDependsOn>_EmbedILLinkXmls;$(PrepareResourcesDependsOn)</PrepareResourcesDependsOn>
    <TargetsTriggeredByCompilation Condition="'$(DesignTimeBuild)' != 'true'">$(TargetsTriggeredByCompilation);ILLinkTrimAssembly</TargetsTriggeredByCompilation>

    <ILLinkDirectory Condition="'$(ILLinkDirectory)' == ''">$(MSBuildProjectDirectory)\ILLink\</ILLinkDirectory>
    <ILLinkTrimAssemblyPath>$(IntermediateOutputPath)$(TargetName)$(TargetExt)</ILLinkTrimAssemblyPath>
    <ILLinkTrimAssemblySymbols>$(IntermediateOutputPath)$(TargetName).pdb</ILLinkTrimAssemblySymbols>
    <ILLinkTrimInputPath>$(IntermediateOutputPath)PreTrim/</ILLinkTrimInputPath>
    <ILLinkTrimInputAssembly>$(ILLinkTrimInputPath)$(TargetName)$(TargetExt)</ILLinkTrimInputAssembly>
    <ILLinkTrimInputSymbols>$(ILLinkTrimInputPath)$(TargetName).pdb</ILLinkTrimInputSymbols>
    <ILLinkTrimOutputPath>$(IntermediateOutputPath)</ILLinkTrimOutputPath>

    <ILLinkDescriptorsXml Condition="'$(ILLinkDescriptorsXml)' == '' and Exists('$(ILLinkDirectory)ILLink.Descriptors.xml')">$(ILLinkDirectory)ILLink.Descriptors.xml</ILLinkDescriptorsXml>
    <!-- ILLink.Descriptors.LibraryBuild.xml files are only used during building the library, not an app. They shouldn't be embedded into the assembly. -->
    <ILLinkDescriptorsLibraryBuildXml Condition="'$(ILLinkDescriptorsLibraryBuildXml)' == '' and Exists('$(ILLinkDirectory)ILLink.Descriptors.LibraryBuild.xml')">$(ILLinkDirectory)ILLink.Descriptors.LibraryBuild.xml</ILLinkDescriptorsLibraryBuildXml>
    <ILLinkDescriptorsXmlIntermediatePath>$(IntermediateOutputPath)ILLink.Descriptors.xml</ILLinkDescriptorsXmlIntermediatePath>

    <ILLinkSubstitutionsXmlIntermediatePath>$(IntermediateOutputPath)ILLink.Substitutions.xml</ILLinkSubstitutionsXmlIntermediatePath>
    <ILLinkLinkAttributesXmlIntermediatePath>$(IntermediateOutputPath)ILLink.LinkAttributes.xml</ILLinkLinkAttributesXmlIntermediatePath>

    <ILLinkSuppressionsXmlPrefix>$(ILLinkDirectory)ILLink.Suppressions</ILLinkSuppressionsXmlPrefix>
    <ILLinkSuppressionsXml>$(ILLinkSuppressionsXmlPrefix).xml</ILLinkSuppressionsXml>
    <ILLinkSuppressionsConfigurationSpecificXml>$(ILLinkSuppressionsXmlPrefix).$(Configuration).xml</ILLinkSuppressionsConfigurationSpecificXml>
    <!-- Only run the trim analyzer on libraries which have been annotated. -->
    <EnableTrimAnalyzer Condition="'$(EnableTrimAnalyzer)' == '' And (Exists('$(ILLinkSuppressionsXml)') Or Exists('$(ILLinkSuppressionsConfigurationSpecificXml)'))">false</EnableTrimAnalyzer>

    <!-- if building a PDB, tell illink to rewrite the symbols file -->
    <ILLinkRewritePDBs Condition="'$(ILLinkRewritePDBs)' == '' and '$(DebugSymbols)' != 'false'">true</ILLinkRewritePDBs>

    <ILLinkResourcesSubstitutionIntermediatePath>$(IntermediateOutputPath)ILLink.Resources.Substitutions.xml</ILLinkResourcesSubstitutionIntermediatePath>
    <GenerateResourcesSubstitutions Condition="'$(GenerateResourcesSubstitutions)' == '' and '$(StringResourcesPath)' != ''">true</GenerateResourcesSubstitutions>
  </PropertyGroup>

   <ItemGroup>
    <ILLinkSuppressionsLibraryBuildXml Include="$(ILLinkSuppressionsXmlPrefix).LibraryBuild.xml"
                                       Condition="Exists('$(ILLinkSuppressionsXmlPrefix).LibraryBuild.xml')" />

    <ILLinkSuppressionsXmls Include="$(ILLinkSuppressionsXml)"
                            Condition="Exists('$(ILLinkSuppressionsXml)')" />
    <ILLinkSuppressionsXmls Include="$(ILLinkSuppressionsConfigurationSpecificXml)"
                            Condition="Exists('$(ILLinkSuppressionsConfigurationSpecificXml)')" />
    <ILLinkSuppressionsXmls Include="@(ILLinkSuppressionsLibraryBuildXml)" />
    <ILLinkSuppressionsXmls Update="@(ILLinkSuppressionsXmls)"
                            TargetPath="%(FileName).$(AssemblyName).xml" />
  </ItemGroup>

  <ItemGroup>
    <ILLinkSubstitutionsXmls Include="$(ILLinkResourcesSubstitutionIntermediatePath)"
                             Condition="'$(GenerateResourcesSubstitutions)' == 'true'" />
    <None Include="@(ILLinkSubstitutionsXmls)" />
    <None Include="$(ILLinkDescriptorsLibraryBuildXml)"
          Condition="'$(ILLinkDescriptorsLibraryBuildXml)' != ''" />
  </ItemGroup>

  <!-- Flow the IsTrimmable property down to consuming projects, in order for oob.proj
       to exclude non trimmable assemblies. -->
  <ItemDefinitionGroup>
    <TargetPathWithTargetPlatformMoniker>
      <IsTrimmable>$(IsTrimmable)</IsTrimmable>
    </TargetPathWithTargetPlatformMoniker>
  </ItemDefinitionGroup>

  <!-- Flow the ILLinkSuppressionsXmls item list down to consuming projects, in order for sfx.proj and oob.proj to
       receive the suppression files. -->
  <Target Name="AnnotateTargetPathWithILLinkSuppressionsXmlsProp"
          AfterTargets="GetTargetPathWithTargetPlatformMoniker">
    <ItemGroup>
      <TargetPathWithTargetPlatformMoniker ILLinkSuppressionsXmls="@(ILLinkSuppressionsXmls->Metadata('FullPath'))" />
    </ItemGroup>
  </Target>

  <Target Name="_EmbedILLinkXmls"
          DependsOnTargets="_CombineILLinkDescriptorsXmls;
                            _CombineILLinkSubstitutionsXmls;
                            _CombineILLinkLinkAttributesXmls">
    <ItemGroup>
      <EmbeddedResource Include="$(ILLinkDescriptorsXml)"
                        LogicalName="ILLink.Descriptors.xml"
                        Condition="'$(ILLinkDescriptorsXml)' != ''" />

      <EmbeddedResource Include="$(ILLinkSubstitutionsXml)"
                        LogicalName="ILLink.Substitutions.xml"
                        Condition="'$(ILLinkSubstitutionsXml)' != ''" />

      <EmbeddedResource Include="$(ILLinkLinkAttributesXml)"
                        LogicalName="ILLink.LinkAttributes.xml"
                        Condition="'$(ILLinkLinkAttributesXml)' != ''" />
    </ItemGroup>
  </Target>

  <UsingTask TaskName="CombineLinkerXmlFiles"
             AssemblyFile="$(ILLinkTasksAssembly)"
             TaskFactory="TaskHostFactory"
             Condition="'$(ILLinkTasksAssembly)' != ''" />
  <Target Name="_CombineILLinkDescriptorsXmls"
          Condition="'@(ILLinkDescriptorsXmls)' != ''"
          Inputs="@(ILLinkDescriptorsXmls)"
          Outputs="$(ILLinkDescriptorsXmlIntermediatePath)">
    <PropertyGroup>
      <ILLinkDescriptorsXml>$(ILLinkDescriptorsXmlIntermediatePath)</ILLinkDescriptorsXml>
    </PropertyGroup>

    <CombineLinkerXmlFiles LinkerXmlFiles="@(ILLinkDescriptorsXmls)"
                           CombinedLinkerXmlFile="$(ILLinkDescriptorsXml)" />

    <ItemGroup>
      <FileWrites Include="$(ILLinkDescriptorsXml)" />
    </ItemGroup>
  </Target>

  <!-- If a library uses string resources, the following target generates a substitution xml that will be embedded on the
  library so that if a consumer wants to run the linker they can specify a feature switch to strip out all resources
  from the assembly. -->
  <Target Name="GenerateResourcesSubstitutionFile"
          Condition="'$(GenerateResourcesSubstitutions)' == 'true'"
          Inputs="$(MSBuildProjectFullPath)"
          Outputs="$(ILLinkResourcesSubstitutionIntermediatePath)">

    <PropertyGroup>
      <ILLinkResourcesSubstitutionTemplate>$(MSBuildThisFileDirectory)ILLink.Substitutions.Resources.template</ILLinkResourcesSubstitutionTemplate>
    </PropertyGroup>

    <WriteLinesToFile File="$(ILLinkResourcesSubstitutionIntermediatePath)"
                      Lines="$([System.IO.File]::ReadAllText('$(ILLinkResourcesSubstitutionTemplate)')
                                                 .Replace('{AssemblyName}', '$(AssemblyName)')
                                                 .Replace('{StringResourcesName}', '$(StringResourcesName)'))"
                      Overwrite="true" />

    <ItemGroup>
      <FileWrites Include="$(ILLinkResourcesSubstitutionIntermediatePath)" />
    </ItemGroup>
  </Target>

  <Target Name="_CombineILLinkSubstitutionsXmls"
          DependsOnTargets="GenerateResourcesSubstitutionFile"
          Condition="'@(ILLinkSubstitutionsXmls)' != ''"
          Inputs="@(ILLinkSubstitutionsXmls)"
          Outputs="$(ILLinkSubstitutionsXmlIntermediatePath)">
    <PropertyGroup>
      <ILLinkSubstitutionsXml>$(ILLinkSubstitutionsXmlIntermediatePath)</ILLinkSubstitutionsXml>
    </PropertyGroup>

    <CombineLinkerXmlFiles LinkerXmlFiles="@(ILLinkSubstitutionsXmls)"
                           CombinedLinkerXmlFile="$(ILLinkSubstitutionsXml)" />

    <ItemGroup>
      <FileWrites Include="$(ILLinkSubstitutionsXml)" />
    </ItemGroup>
  </Target>

  <Target Name="_CombineILLinkLinkAttributesXmls"
          Condition="'@(ILLinkLinkAttributesXmls)' != ''"
          Inputs="@(ILLinkLinkAttributesXmls)"
          Outputs="$(ILLinkLinkAttributesXmlIntermediatePath)">
    <PropertyGroup>
      <ILLinkLinkAttributesXml>$(ILLinkLinkAttributesXmlIntermediatePath)</ILLinkLinkAttributesXml>
    </PropertyGroup>

    <CombineLinkerXmlFiles LinkerXmlFiles="@(ILLinkLinkAttributesXmls)"
                           CombinedLinkerXmlFile="$(ILLinkLinkAttributesXml)" />

    <ItemGroup>
      <FileWrites Include="$(ILLinkLinkAttributesXml)" />
    </ItemGroup>
  </Target>

  <Target Name="PrepareForAssembliesTrim">
    <!-- ILLink.Tasks arguments common to runs for both individual libraries and for the entire runtime pack -->
    <PropertyGroup>
      <!-- don't remove attributes after build, our tooling is not ready for that -->
      <ILLinkArgs>$(ILLinkArgs) --ignore-link-attributes true</ILLinkArgs>
      <!-- ignore unresolved references -->
      <ILLinkArgs>$(ILLinkArgs) --skip-unresolved true</ILLinkArgs>
    </PropertyGroup>

    <!-- When running from Desktop MSBuild, DOTNET_HOST_PATH is not set.
         In this case, explicitly specify the path to the dotnet host. -->
    <PropertyGroup Condition="'$(DOTNET_HOST_PATH)' == ''">
      <!-- This is defined when building in Visual Studio. -->
      <_DotNetHostDirectory>$(NetCoreRoot)</_DotNetHostDirectory>
      <_DotNetHostFileName>$([System.IO.Path]::GetFileName('$(DotNetTool)'))</_DotNetHostFileName>
    </PropertyGroup>
  </Target>

  <!-- Examines the "input assembly" for IL that is unreachable from public API and trims that,
       rewriting the assembly to an "output assembly" -->
  <UsingTask TaskName="ILLink"
             AssemblyFile="$(ILLinkTasksAssembly)"
             TaskFactory="TaskHostFactory"
             Condition="'$(ILLinkTasksAssembly)' != ''" />
  <Target Name="ILLinkTrimAssembly"
          Condition="'$(ILLinkTrimAssembly)' == 'true'"
          DependsOnTargets="PrepareForAssembliesTrim">
    <PropertyGroup>
      <!-- default action for assemblies with IsTrimmable attribute -->
      <ILLinkArgs>$(ILLinkArgs) --trim-mode skip</ILLinkArgs>
      <!-- default action for assemblies without IsTrimmable attribute -->
      <ILLinkArgs>$(ILLinkArgs) --action skip</ILLinkArgs>
      <!-- trim the target assembly -->
      <ILLinkArgs>$(ILLinkArgs) --action link $(TargetName)</ILLinkArgs>
      <ILLinkArgs Condition="'$(ILLinkRewritePDBs)' == 'true' and Exists('$(ILLinkTrimAssemblySymbols)')">$(ILLinkArgs) -b true</ILLinkArgs>
      <!-- pass the non-embedded descriptors xml file on the command line -->
      <ILLinkArgs Condition="'$(ILLinkDescriptorsLibraryBuildXml)' != ''">$(ILLinkArgs) -x "$(ILLinkDescriptorsLibraryBuildXml)"</ILLinkArgs>
      <ILLinkArgs Condition="'$(ILLinkSubstitutionsLibraryBuildXml)' != ''">$(ILLinkArgs) --substitutions "$(ILLinkSubstitutionsLibraryBuildXml)"</ILLinkArgs>
      <ILLinkArgs Condition="'@(ILLinkSuppressionsLibraryBuildXml)' != ''">$(ILLinkArgs) --link-attributes "@(ILLinkSuppressionsLibraryBuildXml->'%(FullPath)', '" --link-attributes "')"</ILLinkArgs>
      <!-- suppress warnings with the following codes:
           IL2008: Could not find type A specified in resource B
           IL2009: Could not find method A in type B specified in resource C
           IL2121: Unnecessary UnconditionalSuppressMessage attribute
           IL2025: Duplicate preserve of A in B
           IL2035: Unresolved assembly A in DynamicDependencyAttribute on B
      -->
      <LinkerNoWarn>$(LinkerNoWarn);IL2008;IL2009;IL2121;IL2025;IL2035</LinkerNoWarn>
      <ILLinkArgs>$(ILLinkArgs) --nowarn $(LinkerNoWarn)</ILLinkArgs>
      <ILLinkArgs Condition="'$(ILLinkDisableIPConstProp)' == 'true'">$(ILLinkArgs) --disable-opt ipconstprop</ILLinkArgs>
    </PropertyGroup>

    <!-- Move the assembly into a subdirectory for ILLink -->
    <Move SourceFiles="$(ILLinkTrimAssemblyPath)"
          DestinationFolder="$(ILLinkTrimInputPath)">
      <Output TaskParameter="MovedFiles" ItemName="FileWrites" />
    </Move>

    <!-- Move the PDB into a subdirectory for ILLink if we are rewriting PDBs -->
    <Move SourceFiles="$(ILLinkTrimAssemblySymbols)"
          DestinationFolder="$(ILLinkTrimInputPath)"
          Condition="'$(ILLinkRewritePDBs)' == 'true' and Exists('$(ILLinkTrimAssemblySymbols)')">
      <Output TaskParameter="MovedFiles" ItemName="FileWrites" />
    </Move>

    <ItemGroup>
      <_DependencyDirectoriesTemp Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory)')" />
      <!-- Remove duplicate directories by batching over them -->
      <!-- Add project references first to give precedence to project-specific files -->
      <_DependencyDirectories Condition="'%(_DependencyDirectoriesTemp.ReferenceSourceTarget)'=='ProjectReference'" Include="%(_DependencyDirectoriesTemp.Identity)" />
      <_DependencyDirectories Condition="'%(_DependencyDirectoriesTemp.ReferenceSourceTarget)'!='ProjectReference'" Include="%(_DependencyDirectoriesTemp.Identity)" />
      <!-- Remove trailing slash to work around response file parsing behavior -->
      <_DependencyDirectoriesSlash Include="@(_DependencyDirectories)">
        <PathWithSlash>$([MSBuild]::EnsureTrailingSlash('%(Identity)'))</PathWithSlash>
      </_DependencyDirectoriesSlash>
      <_DependencyDirectoriesNoSlash Include="@(_DependencyDirectoriesSlash)">
        <PathWithoutSlash>$([System.String]::new('%(PathWithSlash)').TrimEnd($([System.IO.Path]::DirectorySeparatorChar)))</PathWithoutSlash>
      </_DependencyDirectoriesNoSlash>
      <_DependencyDirectories Remove="@(_DependencyDirectories)" />
      <_DependencyDirectories Include="%(_DependencyDirectoriesNoSlash.PathWithoutSlash)" />
      <ILLinkTrimInputAssembly Include="$(ILLinkTrimInputPath)$(TargetName)$(TargetExt)">
        <RootMode>library</RootMode>
      </ILLinkTrimInputAssembly>
    </ItemGroup>

    <PropertyGroup>
      <ILLinkArgs Condition="@(_DependencyDirectories->Count()) > 0">$(ILLinkArgs) -d @(_DependencyDirectories->'"%(Identity)"', ' -d ')</ILLinkArgs>
    </PropertyGroup>

    <ILLink AssemblyPaths=""
            RootAssemblyNames="@(ILLinkTrimInputAssembly)"
            OutputDirectory="$(ILLinkTrimOutputPath)"
            ExtraArgs="$(ILLinkArgs)"
            ToolExe="$(_DotNetHostFileName)"
            ToolPath="$(_DotNetHostDirectory)" />
  </Target>

  <!-- ILLink reporting.
       Only enabled when developer specifies a path to the AsmDiff tool with property AsmDiffCmd.
       EG: AsmDiffCmd=d:\tools\asmdiff\asmdiff.exe
       This is necessary until the AsmDiff tool is ported to .NET Core. -->
  <Target Name="_CreateILLinkTrimAssemblyReports"
          AfterTargets="ILLinkTrimAssembly"
          Condition="'$(AsmDiffCmd)' != ''">
    <PropertyGroup>
      <AsmDiffArgs>$(AsmDiffArgs) $(ILLinkTrimInputAssembly)</AsmDiffArgs>
      <AsmDiffArgs>$(AsmDiffArgs) $(ILLinkTrimAssemblyPath)</AsmDiffArgs>
      <AsmDiffArgs>$(AsmDiffArgs) -includePrivateApis -includeInternalApis -alwaysDiffMembers -diffAttributes</AsmDiffArgs>

      <AsmDiffReport>$(IntermediateOutputPath)$(TargetName).diff.html</AsmDiffReport>
      <AsmDiffReportArgs>$(AsmDiffArgs) -out:$(AsmDiffReport)</AsmDiffReportArgs>
      <AsmDiffReportArgs>$(AsmDiffReportArgs) -unchanged -changed -added -removed</AsmDiffReportArgs>

      <AsmDiffList>$(IntermediateOutputPath)$(TargetName).diff.csv</AsmDiffList>
      <AsmDiffListArgs>$(AsmDiffArgs) -out:$(AsmDiffList)</AsmDiffListArgs>
      <AsmDiffListArgs>$(AsmDiffListArgs) -unchanged -changed -added -removed </AsmDiffListArgs>
      <AsmDiffListArgs>$(AsmDiffListArgs) -diffWriter:CSV</AsmDiffListArgs>
    </PropertyGroup>

    <Exec Command="$(AsmDiffCmd) $(AsmDiffReportArgs)" />
    <Message Text="Assembly trimming diff: $(AsmDiffReport)" />
    <Exec Command="$(AsmDiffCmd) $(AsmDiffListArgs)" />
    <Message Text="Assembly trimming report: $(AsmDiffList)" />
  </Target>

</Project>
